#ifndef __COMM_HPP__
#define __COMM_HPP__
#include <iostream>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <cassert>
#include <string>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>

using namespace std;

#define PATHNAME "."
#define PROJID 0x2333

#define CLIENT 0
#define SERVER 1

const int gsize = 4097;

class ShareMem
{
private:
    int _identity; // 当前进程的身份，0表示client，1表示server
    void *_start;  // 共享内存虚拟地址首地址
    int _shmid;    // 共享内存id

private:
    key_t getKey()
    {
        key_t k = ftok(PATHNAME, PROJID);
        if (k == -1)
        {
            cerr << "error: " << errno << " : " << strerror(errno) << endl;
            exit(1);
        }
        return k;
    }

    static int shmgetHelper(key_t key, size_t size, int shmflg)
    {
        int shmid = shmget(key, size, shmflg);
        if (shmid == -1)
        {
            cout << "[Error] " << errno << ": " << strerror(errno) << endl;
            exit(2);
        }
        return shmid;
    }

    int createShm(key_t key, int size)
    {
        umask(0);
        return shmgetHelper(key, size, IPC_CREAT | IPC_EXCL | 0666);
    }

    int getShm(key_t key, int size)
    {
        return shmgetHelper(key, size, IPC_CREAT);
    }

    void delShm(int shmid)
    {
        int n = shmctl(shmid, IPC_RMID, nullptr);
        assert(n != -1);
        (void)n;
    }

    void *attachShm(int shmid)
    {
        void *start = shmat(shmid, nullptr, 0);
        return start;
    }

    void detachShm(void *start)
    {
        int n = shmdt(start);
        assert(n != -1);
        (void)n;
    }

public:
    ShareMem(int id) : _identity(id)
    {
        key_t k = getKey();
        if (id == SERVER)
            _shmid = createShm(k, gsize);
        else
            _shmid = getShm(k, gsize);
        _start = attachShm(_shmid);
    }
    ~ShareMem()
    {
        detachShm(_start);
        if (_identity == SERVER)
            delShm(_shmid);
    }

    // 获取共享内存的虚拟地址首地址
    void *getStart()
    {
        return _start;
    }
};

#endif